#include "EbnModel.h"
#include <time.h>
#include <QEvent>

CEbnModel* CEbnModel::mpSingleInstance = NULL;

CEbnModel::CEbnModel(QObject *parent) :
    QObject(parent)
{
    mTime = 0;
    mStepInterval = DEFAULT_INTERVAL;
    mRunning = false;
    mModelStarted = false;
    mUpdateTimerId = 0;
    mpNetParamManager = new CEbnNetParamManager();
    mpActionMsgReceiver = new CEbnActionMsgReceiver();

}

CEbnModel::~CEbnModel(void)
{
    if (mpNetParamManager)
    {
        delete mpNetParamManager;
        mpNetParamManager = NULL;
    }

    if (mpActionMsgReceiver)
    {
        delete mpActionMsgReceiver;
        mpActionMsgReceiver = NULL;
    }
}

CEbnNetParamManager * CEbnModel::getNetParamManager(void)
{
    return mpNetParamManager;
}

CEbnActionMsgReceiver * CEbnModel::getActionMsgReceiver(void)
{
    return mpActionMsgReceiver;
}

void CEbnModel::setStepInterval(int sec)
{
    mStepInterval = sec * UNIT_PER_SEC;
}

int CEbnModel::getStepInterval(void)
{
    return mStepInterval;
}

void CEbnModel::addCompetenceModule(CEbnCompetenceModule * pCompetenceModule)
{
    bool exist = hasCompetenceModule(pCompetenceModule);
    if (false == exist)
    {
        mCompetenceModules.push_back(pCompetenceModule);
    }

}

void CEbnModel::addLink(CEbnLink * pLink)
{
    bool exist = hasLink(pLink);
    if (false == exist)
    {
        mLinks.push_back(pLink);
    }
}

void CEbnModel::addGoal(CEbnGoal * pGoal)
{
    bool exist = hasGoal(pGoal);
    if (false == exist)
    {
        mGoals.push_back(pGoal);
    }
}

CEbnModel * CEbnModel::getModel()
{
    if(NULL == mpSingleInstance)
    {
        mpSingleInstance = new CEbnModel();
    }

    return mpSingleInstance;
}


void CEbnModel::addReceptor(CEbnReceptor * pReceptor)
{
    bool exist = hasReceptor(pReceptor);
    if (false == exist)
    {
        mReceptors.push_back(pReceptor);
    }
}


bool CEbnModel::hasCompetenceModule(CEbnCompetenceModule * pCompetenceModule)
{
    vector<CEbnCompetenceModule *>::iterator itCompetenceModule;

    for (itCompetenceModule = mCompetenceModules.begin(); itCompetenceModule != mCompetenceModules.end(); itCompetenceModule++)
    {
        if ((*itCompetenceModule) == pCompetenceModule)
        {
            return true;
        }
    }

    return false;
}

bool CEbnModel::hasGoal(CEbnGoal * pGoal)
{
    vector<CEbnGoal *>::iterator itGoal;

    for (itGoal = mGoals.begin(); itGoal != mGoals.end(); itGoal++)
    {
        if ((*itGoal) == pGoal)
        {
            return true;
        }
    }

    return false;

}

bool CEbnModel::hasLink(CEbnLink * pLink)
{
    vector<CEbnLink *>::iterator itLink;

    for (itLink = mLinks.begin(); itLink != mLinks.end(); itLink++)
    {
        if ((*itLink) == pLink)
        {
            return true;
        }
    }

    return false;
}

bool CEbnModel::hasReceptor(CEbnReceptor * pReceptor)
{
    vector<CEbnReceptor *>::iterator itReceptor;

    for (itReceptor = mReceptors.begin(); itReceptor != mReceptors.end(); itReceptor++)
    {
        if ((*itReceptor) == pReceptor)
        {
            return true;
        }
    }

    return false;
}

CEbnPredecessorLink * CEbnModel::createPredecessorLink(CEbnCompetenceModule * from, CEbnCompetenceModule * to, CEbnEffect * pFromEffect, CEbnCondition * pToCondition)
{
    CEbnPredecessorLink * pLink = new CEbnPredecessorLink(pFromEffect, pToCondition);
    pLink->setSrcCompetenceModule(from);
    pLink->setDstCompetenceModule(to);

    return pLink;
}

CEbnSuccessorLink * CEbnModel::createSuccessorLink(CEbnCompetenceModule * from, CEbnCompetenceModule * to, CEbnCondition * pFromCondition, CEbnEffect * pToEffect)
{
    CEbnSuccessorLink * pLink = new CEbnSuccessorLink(pFromCondition, pToEffect);
    pLink->setSrcCompetenceModule(from);
    pLink->setDstCompetenceModule(to);

    return pLink;
}

CEbnConflictorLink * CEbnModel::createConflictorLink(CEbnCompetenceModule * from, CEbnCompetenceModule * to, CEbnCondition * pFromCondition, CEbnEffect * pToEffect)
{
    CEbnConflictorLink * pLink = new CEbnConflictorLink(pFromCondition, pToEffect);
    pLink->setSrcCompetenceModule(from);
    pLink->setDstCompetenceModule(to);

    return pLink;
}

CEbnGoalLink * CEbnModel::createGoalLink(CEbnGoal * from, CEbnCompetenceModule * to, CEbnCondition * pFromCondition, CEbnEffect * pToEffect)
{
    CEbnGoalLink * pLink = new CEbnGoalLink(pFromCondition, pToEffect);
    pLink->setSrcGoal(from);
    pLink->setDstCompetenceModule(to);

    return pLink;
}

CEbnReceptorLink * CEbnModel::createReceptorLink(CEbnReceptor * from, CEbnCompetenceModule * to, CEbnReception * pFromReception, CEbnCondition * pToCondition)
{
    CEbnReceptorLink * pLink = new CEbnReceptorLink(pFromReception, pToCondition);
    pLink->setSrcReceptor(from);
    pLink->setDstCompetenceModule(to);

    return pLink;
}

void CEbnModel::createLink(CEbnReceptor * from, CEbnCompetenceModule * to)
{
    CEbnLink * pLink = NULL;
    CEbnReception * pFromReception = NULL;
    CEbnCondition * pToCondition = NULL;

    if ((NULL == from) || (NULL == to))
    {
        return;
    }

    pFromReception = from->getReception();
    for (int i = 1; i <= to->getConditionCount(); i++)
    {
        pToCondition = to->getCondition(i);

        if (pFromReception->isIdentical((CEbnProposition*)pToCondition))
        {
            pLink = createReceptorLink(from, to, pFromReception, pToCondition);
            addLink(pLink);
        }
    }

}

void CEbnModel::createLink(CEbnGoal * from, CEbnCompetenceModule * to)
{
    CEbnLink * pLink = NULL;
    CEbnCondition * pFromCondition = NULL;
    CEbnEffect * pToEffect = NULL;

    if ((NULL == from) || (NULL == to))
    {
        return;
    }


    pFromCondition = from->getCondition();

    for (int i = 1; i <= to->getPostEffectCount(); i++)
    {
        pToEffect = to->getPostEffect(i);

        if (pFromCondition->isSame((CEbnProposition*)pToEffect))
        {
            if (pFromCondition->isIdentical((CEbnProposition*)pToEffect))
            {
                pLink = createGoalLink(from, to, pFromCondition, pToEffect);
            }
            else if (pFromCondition->isInverse((CEbnProposition*)pToEffect))
            {
                // pLink = createConflictorLink(from, to, from->getCondition(), to->getPostEffect(i));
            }

            addLink(pLink);

        }
        else
        {
            continue;
        }
    }

}

void CEbnModel::createLink(CEbnCompetenceModule * from, CEbnCompetenceModule * to)
{
    CEbnLink * pLink = NULL;
    CEbnCondition * pFromCondition = NULL;
    CEbnEffect * pToEffect = NULL;

    if ((NULL == from) || (NULL == to))
    {
        return;
    }

    for (int i = 1; i <= from->getConditionCount(); i++)
    {
        pFromCondition = from->getCondition(i);

        for (int j = 1; j <= to->getPostEffectCount(); j++)
        {
            pToEffect = to->getPostEffect(j);

            if (pFromCondition->isSame((CEbnProposition*)pToEffect))
            {
                if (pFromCondition->isIdentical((CEbnProposition*)pToEffect))
                {
                    pLink = createSuccessorLink(from, to, from->getCondition(i), to->getPostEffect(j));
                    addLink(pLink);

                    pLink = createPredecessorLink(to, from, to->getPostEffect(j), from->getCondition(i));
                    addLink(pLink);
                }
                else if (pFromCondition->isInverse((CEbnProposition*)pToEffect))
                {
                    pLink = createConflictorLink(from, to, from->getCondition(i), to->getPostEffect(j));
                    addLink(pLink);
                }

            }
            else
            {
                continue;
            }
        }
    }

}

void CEbnModel::initializeLinks(void)
{
    vector<CEbnCompetenceModule *>::iterator itCMFrom;
    vector<CEbnCompetenceModule *>::iterator itCMTo;
    vector<CEbnGoal *>::iterator itGoal;
    vector<CEbnReceptor *>::iterator itReceptor;

    for (itGoal = mGoals.begin(); itGoal != mGoals.end(); itGoal++)
    {
        for (itCMTo = mCompetenceModules.begin(); itCMTo != mCompetenceModules.end(); itCMTo++)
        {
            createLink((*itGoal), (*itCMTo));
        }
    }

    for (itReceptor = mReceptors.begin(); itReceptor != mReceptors.end(); itReceptor++)
    {
        for (itCMTo = mCompetenceModules.begin(); itCMTo != mCompetenceModules.end(); itCMTo++)
        {
            createLink((*itReceptor), (*itCMTo));
        }
    }


    for (itCMFrom = mCompetenceModules.begin(); itCMFrom != mCompetenceModules.end(); itCMFrom++)
    {
        for (itCMTo = mCompetenceModules.begin(); itCMTo != mCompetenceModules.end(); itCMTo++)
        {

            if ((* itCMFrom) == (* itCMTo))
                continue;

            createLink((*itCMFrom), (*itCMTo));
        }
    }

    // update Link count
    for (itCMTo = mCompetenceModules.begin(); itCMTo != mCompetenceModules.end(); itCMTo++)
    {
        (*itCMTo)->updateLinkCnt();
    }
}

void CEbnModel::uninitializeLinks(void)
{
    CEbnLink * pLink = NULL;
    vector<CEbnLink *>::iterator itLink;

    for (itLink = mLinks.begin(); itLink != mLinks.end(); itLink++)
    {
        pLink = (* itLink);

        delete pLink;
        pLink = NULL;
    }

    mLinks.clear();

}

void CEbnModel::initializeHistByGoal(void)
{
    vector<CEbnGoal *>::iterator itGoal;
    vector<CEbnCompetenceModule *>::iterator itCM;

    CEbnCMEnergyHistFromGoal * pHistFromGoal;

    for (itGoal = mGoals.begin(); itGoal != mGoals.end(); itGoal++)
    {
        pHistFromGoal = new CEbnCMEnergyHistFromGoal((*itGoal));

        for (itCM = mCompetenceModules.begin(); itCM != mCompetenceModules.end(); itCM++)
        {
            pHistFromGoal->addCMEnergyHist((*itCM));
        }

        mHistsByGoal.push_back(pHistFromGoal);
    }
}

void CEbnModel::uninitializeHistByGoal(void)
{
    vector<CEbnCMEnergyHistFromGoal *>::iterator itHist;

    for (itHist = mHistsByGoal.begin(); itHist != mHistsByGoal.end(); itHist++)
    {
        CEbnCMEnergyHistFromGoal * pEnergyHistByGoal = (* itHist);
        delete pEnergyHistByGoal;
    }

    mHistsByGoal.clear();
}

bool CEbnModel::saveToHist(CEbnGoal * goal, CEbnCompetenceModule * cm, float activation)
{
    vector<CEbnCMEnergyHistFromGoal *>::iterator itHist;
    for (itHist = mHistsByGoal.begin(); itHist != mHistsByGoal.end(); itHist++)
    {
        if ((*itHist)->isHostGoal(goal))
        {
            if ((*itHist)->saveToEnergyHist(cm, activation))
            {
                return true;
            }
        }
    }

    return false;
}

float CEbnModel::getActivationHist(CEbnGoal * goal, CEbnCompetenceModule * cm, int time)
{
    vector<CEbnCMEnergyHistFromGoal *>::iterator itHist;
    for (itHist = mHistsByGoal.begin(); itHist != mHistsByGoal.end(); itHist++)
    {
        if ((*itHist)->isHostGoal(goal))
        {
            return (*itHist)->getEnergyHist(cm, time);
        }
    }

    return 0;

}

int CEbnModel::getStepCount()
{
    return mTime;
}

void CEbnModel::increaseStepCount()
{
    mTime ++;
}

void CEbnModel::resetStepCount()
{
    mTime = 0;
}

bool CEbnModel::start(void)
{
    qDebug("CEbnModel::start");
    if (false == mModelStarted)
    {

        resetStepCount();
        reset();

        initializeLinks();
        initializeHistByGoal();

        mModelStarted = true;
    }

    return true;

}

void CEbnModel::wait(int sec)
{
    clock_t endwait;
    endwait = clock () + sec * CLOCKS_PER_SEC ;
    while (clock() < endwait) {}
}

void CEbnModel::pause(void)
{
    qDebug("CEbnModel::pause");
    if (false == mModelStarted)
    {
        return;
    }

    if (mRunning)
    {

        if(0 != mUpdateTimerId)
        {
            killTimer(mUpdateTimerId);
            mUpdateTimerId = 0;
        }
        mRunning = false;

    }
}

void CEbnModel::stop(void)
{
    qDebug("CEbnModel::stop");

    // pause it before stop
    pause();

    if (mModelStarted)
    {
        uninitializeHistByGoal();
        uninitializeLinks();
    }
}

void CEbnModel::updateActivation(void)
{
    vector<CEbnCompetenceModule *>::iterator itCM;
    vector<CEbnGoal *>::iterator itGoal;
    CEbnCompetenceModule * pCM;
    float activation = 0;

    // Update executability
    for (itCM = mCompetenceModules.begin(); itCM != mCompetenceModules.end(); itCM++)
    {
        pCM = (*itCM);
        pCM->setToExecute(false);
        pCM->updateExecutability();
    }

    for (itCM = mCompetenceModules.begin(); itCM != mCompetenceModules.end(); itCM++)
    {
        pCM = (*itCM);

        activation = pCM->updateActivation();

        /*
            // deteremine activation
            saveToHist(pGoal, pCM, activation);
         */



    }

    increaseStepCount();
}

void CEbnModel::run(void)
{
    qDebug("CEbnModel::run");
    if (false == mModelStarted)
    {
        return;
    }

    if (false == mRunning)
    {

        mUpdateTimerId = startTimer(mStepInterval);
        qDebug(" set timer id %d ", mUpdateTimerId);
        mRunning = true;
    }

}

void CEbnModel::update()
{
    bool actionSelected = false;
    qDebug("CEbnModel::update()", mTime);
    updateActivation();
    actionSelected = selectActionToDo();

    if (actionSelected)
    {
        mpNetParamManager->resetThresholdDecaying();
    }
    else
    {
        mpNetParamManager->updateThresholdDecaying();
    }

    emit notifyUpdated();
}

void CEbnModel::timerEvent( QTimerEvent *event )
{
    if(mUpdateTimerId == event->timerId())
    {
        update();
    }
}

void CEbnModel::reset(void)
{
    vector<CEbnCompetenceModule *>::iterator itCM;
    for (itCM = mCompetenceModules.begin(); itCM != mCompetenceModules.end(); itCM++)
    {
        (*itCM)->resetActivation();
    }
}

void CEbnModel::clear(void)
{
    vector<CEbnCompetenceModule *>::iterator itCM;
    vector<CEbnGoal *>::iterator itGoal;
    vector<CEbnReceptor *>::iterator itReceptor;

    CEbnCompetenceModule * pCM = NULL;
    CEbnGoal             * pGoal = NULL;
    CEbnReceptor        * pReceptor = NULL;

    for (itCM = mCompetenceModules.begin(); itCM != mCompetenceModules.end(); itCM++)
    {
        pCM = (*itCM);

        if (pCM)
        {
            delete pCM;
            pCM = NULL;
        }
    }

    mCompetenceModules.clear();

    for (itGoal = mGoals.begin(); itGoal != mGoals.end(); itGoal++)
    {
        pGoal = (*itGoal);

        if (pGoal)
        {
            delete pGoal;
            pGoal = NULL;
        }
    }

    mGoals.clear();

    for (itReceptor = mReceptors.begin(); itReceptor != mReceptors.end(); itReceptor++)
    {
        pReceptor = (*itReceptor);

        if (pReceptor)
        {
            delete pReceptor;
            pReceptor = NULL;
        }
    }

    mReceptors.clear();


}



bool CEbnModel::selectActionToDo(void)
{
    // If no action is selected, return false

    CEbnCompetenceModule * pSelectedCM = NULL;
    float mSelectedActivation = 0.0;
    float mModuleActivation = 0.0;
    float mModuleExecutability = 0.0;

    vector<CEbnCompetenceModule *>::iterator itCM;

    for (itCM = mCompetenceModules.begin(); itCM != mCompetenceModules.end(); itCM++)
    {

        mModuleActivation = (*itCM)->getActivation();
        mModuleExecutability = (*itCM)->getExecutability();

        if(mModuleExecutability > mpNetParamManager->getExecutabilityThreshold())
        {
            if(mModuleActivation > mpNetParamManager->getActivationThreshold())
            {
                if (NULL == pSelectedCM)
                {
                    pSelectedCM = (*itCM);
                    mSelectedActivation = mModuleActivation;
                }
                else
                {
                    if (mModuleActivation > mSelectedActivation)
                    {
                        pSelectedCM = (*itCM);
                        mSelectedActivation = mModuleActivation;
                    }
                }
            }
        }

    }

    // found competence module to select
    if (pSelectedCM)
    {
        pSelectedCM->executeModule();
        return true;
    }

    return false;
}

int CEbnModel::getGoalCount()
{
    return (int)mGoals.size();
}

int CEbnModel::getCompetenceModuleCount()
{
    return (int)mCompetenceModules.size();
}

int CEbnModel::getLinkCount()
{
    return (int)mLinks.size();
}

int CEbnModel::getReceptorCount()
{
    return (int)mReceptors.size();
}

CEbnGoal * CEbnModel::getGoal(int index)
{
    if ((index < 0) || (index >= getGoalCount()))
    {
        return NULL;
    }

    return (CEbnGoal *)mGoals.at(index);
}

CEbnCompetenceModule * CEbnModel::getCompetenceModule(int index)
{
    if ((index < 0) || (index >= getCompetenceModuleCount()))
    {
        return NULL;
    }

    return (CEbnCompetenceModule *)mCompetenceModules.at(index);
}

CEbnLink * CEbnModel::getLink(int index)
{
    if ((index < 0) || (index >= getLinkCount()))
    {
        return NULL;
    }

    return (CEbnLink *)mLinks.at(index);
}

CEbnReceptor * CEbnModel::getReceptor(int index)
{
    if ((index < 0) || (index >= getReceptorCount()))
    {
        return NULL;
    }

    return (CEbnReceptor *)mReceptors.at(index);
}

void CEbnModel::removeReceptor(CEbnReceptor * pReceptor)
{
    vector<CEbnReceptor *>::iterator itReceptor;
    for (vector<CEbnReceptor *>::iterator it = mReceptors.begin(); it != mReceptors.end(); it++)
    {
        if ((* it) == pReceptor)
        {
            itReceptor = it;
        }
    }

    mReceptors.erase(itReceptor);
}

void CEbnModel::removeCompetenceModule(CEbnCompetenceModule * pModule)
{
    vector<CEbnCompetenceModule *>::iterator itCompetenceModule;
    for (vector<CEbnCompetenceModule *>::iterator it = mCompetenceModules.begin(); it != mCompetenceModules.end(); it++)
    {
        if ((* it) == pModule)
        {
            itCompetenceModule = it;
        }
    }

    mCompetenceModules.erase(itCompetenceModule);
}

void CEbnModel::removeGoal(CEbnGoal * pGoal)
{
    vector<CEbnGoal *>::iterator itGoal;
    for (vector<CEbnGoal *>::iterator it = mGoals.begin(); it != mGoals.end(); it++)
    {
        if ((* it) == pGoal)
        {
            itGoal = it;
        }
    }

    mGoals.erase(itGoal);
}

CEbnReceptor * CEbnModel::findReceptor(CEbnWorldState * worldState)
{
    vector<CEbnReceptor *>::iterator itReceptor;
    for (itReceptor = mReceptors.begin(); itReceptor != mReceptors.end(); itReceptor++)
    {
        if ((* itReceptor)->hasWorldState(worldState))
        {
            return (* itReceptor);
        }
    }

    return NULL;
}

CEbnGoal * CEbnModel::findGoal(CEbnWorldState * worldState)
{
    vector<CEbnGoal *>::iterator itGoal;
    for (itGoal = mGoals.begin(); itGoal != mGoals.end(); itGoal++)
    {
        if ((* itGoal)->hasWorldState(worldState))
        {
            return (* itGoal);
        }
    }

    return NULL;
}

CEbnCompetenceModule * CEbnModel::findCompetenceModule(CEbnAction * pAction)
{
    vector<CEbnCompetenceModule *>::iterator itModule;
    for (itModule = mCompetenceModules.begin(); itModule != mCompetenceModules.end(); itModule++)
    {
        if ((* itModule)->hasAction(pAction))
        {
            return (* itModule);
        }
    }

    return NULL;
}

bool CEbnModel::isModelStarted(void)
{
    return mModelStarted;
}

bool CEbnModel::isModelRunning(void)
{
    return mRunning;
}

QString CEbnModel::getCompetenceModuleInfo(int index)
{
    CEbnCompetenceModule * cm = getCompetenceModule(index);
    QString info;

    if (cm)
    {
        info = QString::fromLocal8Bit(cm->getActionName().c_str());
       info += ">>A:";
       info += QString("%1").arg(cm->getActivation());
       info += ">>E:";
       info += QString("%1").arg(cm->getExecutability());
       info += ">>";
       if (cm->isSelectedToExecute())
       {
           info += QString("TRUE");
       }
       else
       {
           info += QString("FALSE");
       }

       return info;
    }

    return "NULL";
}

QString CEbnModel::getGoalInfo(int index)
{
    CEbnGoal * goal = getGoal(index);
    QString info;

    if (goal)
    {
        info = QString::fromLocal8Bit(goal->getCondition()->getName().c_str());
        info += ">>";
        if (goal->isReached())
        {
            info += QString("TRUE");
        }
        else
        {
            info += QString("FALSE");
        }

        return info;
    }

    return "NULL";
}

QString CEbnModel::getReceptorInfo(int index)
{
    CEbnReceptor * receptor = getReceptor(index);
    QString info;

    if (receptor)
    {
        info = QString::fromLocal8Bit(receptor->getReception()->getName().c_str());
        info += ">>";
        info += QString("%1").arg(receptor->getReception()->getValue());

        return info;
    }

    return "NULL";
}

QString CEbnModel::getModelInfo()
{
    QString info;
    info = QString("At time %1 :").arg(mTime);
    vector<CEbnCompetenceModule *>::iterator itModule;
    for (itModule = mCompetenceModules.begin(); itModule != mCompetenceModules.end(); itModule++)
    {
        if ((*itModule)->isSelectedToExecute())
        {
            info += QString::fromLocal8Bit((*itModule)->getActionName().c_str()) + QString(" is selected! ");
        }
    }


    return info;
}
